/**********************************************************************
 * FILE : BaseServiceImpl.java
 * CREATE DATE : 2008-12-10
 * DESCRIPTION :
 *		
 *      
 * CHANGE HISTORY LOG
 *---------------------------------------------------------------------
 * NO.|    DATE    |     NAME     |     REASON     | DESCRIPTION
 *---------------------------------------------------------------------
 * 1  | 2008-06-06 |  ZhangGuojie  |    创建草稿版本
 *---------------------------------------------------------------------              
 * Licensed Materials - Property of IBM
 * 
 * 6949-31G
 * 
 * (C) Copyright IBM Corp. 2007 All Rights Reserved. (C) Copyright State of New
 * York 2002 All Rights Reserved.
 * 
 * US Government Users Restricted Rights - Use, duplication or disclosure
 * restricted by GSA ADP Schedule Contract with IBM Corp.
 *
 ******************************************************************************/
package com.xcesys.extras.remoting;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

import com.xcesys.extras.core.dao.model.AbstractEntity;
import com.xcesys.extras.core.exception.SystemException;
import com.xcesys.extras.core.model.PageResult;
import com.xcesys.extras.core.model.QueryCriteria;
import com.xcesys.extras.core.service.ICrudService;
import com.xcesys.extras.dataobject.AbstractDataObject;
import com.xcesys.extras.dataobject.PaginationContent;
import com.xcesys.extras.dataobject.QueryObject;

/**
 * <p>
 * Base remoting service class is a super class for all web service implementor
 * classes.
 * 
 * <p>
 * This class is intended to be sub-class ,and also intended to be extended in
 * the future.
 * 
 * @author Danne
 */
public abstract class BaseRemotingServiceCrudImpl<D extends AbstractDataObject, M extends AbstractEntity>
		extends BaseRemotingServiceImpl implements RemotingCrudService<D>,
		InitializingBean {

	/**
	 * Class of data object.
	 */
	private Class<D> doClazz;

	/**
	 * Class of persistence object.
	 */
	private Class<M> pClazz;

	@Autowired(required = false)
	@Qualifier("dozerDataObjectHelper")
	IDataObjectHelper dataHelper = null;

	@SuppressWarnings("unchecked")
	@Override
	public void afterPropertiesSet() throws Exception {
		if (ParameterizedType.class.isAssignableFrom(getClass()
				.getGenericSuperclass().getClass())) {
			Type[] args = ((ParameterizedType) getClass()
					.getGenericSuperclass()).getActualTypeArguments();
			doClazz = (Class<D>) args[0];
			pClazz = (Class<M>) args[1];
		}
	}

	@Override
	public D create(D d) {
		M m = getCrudService().create(fromDataObject(d));
		super.auditCreate(m);
		return fromModel(m);
	}

	@Override
	public Collection<D> createOrUpdate(Collection<D> list) {
		Collection<M> models = fromDataObject(list);
		getCrudService().createOrUpdate(models);
		super.auditUpdate(models);
		return fromModel(models);
	}

	@Override
	public void delete(Long id) {
		try {
			M m = getCrudService().findById(id);
			getCrudService().delete(m);
			super.auditRemove(m);
		} catch (SystemException e) {
			log.warn("Try to delete object has id:" + id + " failed.", e);
			throw e;
		}
	}

	@Override
	public void deleteByIds(List<Long> ids) {
		if (ids == null || ids.isEmpty()) {
			return;
		}
		List<M> models = getCrudService().findByIds(ids.toArray(new Long[] {}));
		getCrudService().delete(models);
		super.auditRemove(models);
	}

	@Override
	public List<D> findAll() {
		return (List<D>) fromModel(getCrudService().findAll());
	}

	@Override
	public PaginationContent<D> findByCriteria(QueryObject query) {
		// copy query properties.
		QueryCriteria<String, Object> q = new QueryCriteria<String, Object>();
		q.setDirection(query.getDirection());
		q.setPage(query.getPage());
		q.setPageSize(query.getPageSize());
		q.setSort(query.getSort());
		q.setStartIndex(query.getStartIndex());
		// FIXED: WAS MAP PUT ALL BUG.
		for (String key : query.getMap().keySet()) {
			Object obj = query.getMap().get(key);
			if (obj != null) {
				q.put(key, obj);
			}
		}
		// execute query
		PageResult<M> pr = getCrudService().findByCriteria(q);

		// result.
		PaginationContent<D> prd = new PaginationContent<D>();
		if (pr != null) {
			prd.setTotalCount(pr.getTotalCount());
			prd.setTotalPage(pr.getTotalPage());
			prd.setPageSize(query.getPageSize());
			prd.setStartIndex(pr.getStartIndex());
			List<M> contents = pr.getContents();
			if (contents != null) {
				prd.setContents((List<D>) fromModel(contents));
			}
		}
		return prd;
	}

	@Override
	public D findById(Long id) {
		return fromModel(getCrudService().findById(id));
	}

	@Override
	public List<D> findByIds(Long[] ids) {
		return (List<D>) fromModel(getCrudService().findByIds(ids));
	}

	@SuppressWarnings("rawtypes")
	protected <T> Collection<T> fromDataObject(Class<T> clazz, Collection froms) {
		return getDataHelper().copyProperties(clazz, froms);
	}

	protected <T> T fromDataObject(Class<T> clazz, Object from) {
		return getDataHelper().copyProperties(clazz, from);
	}

	protected Collection<M> fromDataObject(Collection<D> froms) {
		return getDataHelper().copyProperties(pClazz, froms);
	}

	/**
	 * Copy data from data object model object.
	 * 
	 * @param d
	 *            data object.
	 * 
	 * @return model object
	 */
	protected M fromDataObject(D d) {
		return getDataHelper().copyProperties(pClazz, d);
	}

	protected M fromDataObject(M m, D d) {
		getDataHelper().copyProperties(m, d);
		return m;
	}

	@SuppressWarnings("rawtypes")
	protected <T> Collection<T> fromModel(Class<T> clazz, Collection froms) {
		Collection<T> collection = getDataHelper().copyProperties(clazz, froms);
		return collection;

	}

	protected <T> T fromModel(Class<T> clazz, Object from) {
		return getDataHelper().copyProperties(clazz, from);

	}

	protected Collection<D> fromModel(Collection<M> froms) {
		Collection<D> collection = getDataHelper().copyProperties(doClazz,
				froms);
		return collection;
	}

	protected D fromModel(D d, M m) {
		getDataHelper().copyProperties(d, m);
		return d;
	}

	/**
	 * Copy data from model object to data object.
	 * 
	 * @param d
	 *            data object.
	 * @param m
	 *            model object
	 * @return data object
	 */
	protected D fromModel(M m) {
		return getDataHelper().copyProperties(doClazz, m);
	}

	@Override
	public D getById(Long id) {
		return fromModel(getCrudService().getById(id));
	}

	protected abstract ICrudService<M> getCrudService();

	/**
	 * Returns an instance of {@link IDataObjectHelper} that help to copy data
	 * from model object to data object, or vice versa.
	 * 
	 * @return
	 */
	protected IDataObjectHelper getDataHelper() {
		return dataHelper;
	}

	@Override
	public D update(D d) {
		M m = getCrudService().update(fromDataObject(d));
		super.auditUpdate(m);
		return fromModel(m);
	}
}